package org.alex.spring.core.util;

import org.alex.spring.core.annotation.Bean;
import org.alex.spring.core.annotation.Component;
import org.alex.spring.core.exception.BeanDefinitionException;

import javax.annotation.PreDestroy;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author : alex
 */
public class ClassUtils {

    /**
     * 递归查询Annotation
     */
    public static <A extends Annotation> A findAnnotation(Class<?> target, Class<A> annoClass) {
        A a = target.getAnnotation(annoClass);
        for (Annotation anno : target.getAnnotations()) {
            Class<? extends Annotation> annoType = anno.annotationType();

            if (annoType.getPackageName().equals("java.lang.annotation")) {
                continue;
            }

            A found = findAnnotation(annoType, annoClass);

            if (found == null) {
                continue;
            }

            if (a != null) { // 标注了多层注解
                throw new BeanDefinitionException("Duplicate @" + annoClass.getSimpleName() +" found on class " + target.getSimpleName());
            }
            a = found;
        }
        return a;
    }

    public static String getBeanName(Class<?> clazz) {
        String name = "";
        // 查询@Component
        Component component = clazz.getAnnotation(Component.class);
        if (component != null) {
            name = component.value();
        } else {
            // 未找到@Component 继续在注解中寻找
            for (Annotation anno : clazz.getAnnotations()) {
                component = findAnnotation(anno.annotationType(), Component.class);
                if (component != null) {
                    name = component.value();
                }
            }
        }
        if (name.isEmpty()) {
            // 默认驼峰
            name = clazz.getSimpleName();
            name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
        }

        return name;
    }

    /**
     * 获取注解对应的执行方法 主要用于@PostContructor 和@PreDestroy
     */
    public static Method findAnnotationMethod(Class<?> clazz, Class<? extends Annotation> annoClass) {
        List<Method> ms = Arrays.stream(clazz.getDeclaredMethods()).filter(m -> m.isAnnotationPresent(annoClass)).map(m -> {
            if (m.getParameterCount() != 0) {
                throw new BeanDefinitionException(String.format("Method '%s' with @%s must not have argument: %s", m.getName(), annoClass.getSimpleName(), clazz.getName()));
            }
            return m;
        }).collect(Collectors.toList());

        if (ms.isEmpty()) {
            return null;
        }
        if (ms.size() != 1) {
            throw new BeanDefinitionException(String.format("Multiple methods with @%s found in class: %s", annoClass.getSimpleName(), clazz.getName()));
        }
        return ms.get(0);
    }

    public static String getBeanName(Method method) {
        Bean bean = method.getAnnotation(Bean.class);
        String name = bean.value();
        if (name.isEmpty()) {
            name = method.getName();
        }
        return name;
    }
}
